package org.young.admin.common.config.shiro;

import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.filter.InvalidRequestFilter;
import org.apache.shiro.web.filter.mgt.DefaultFilter;
import org.apache.shiro.web.filter.mgt.FilterChainManager;
import org.apache.shiro.web.filter.mgt.FilterChainResolver;
import org.apache.shiro.web.filter.mgt.PathMatchingFilterChainResolver;
import org.apache.shiro.web.mgt.WebSecurityManager;
import org.apache.shiro.web.servlet.AbstractShiroFilter;
import org.springframework.beans.factory.BeanInitializationException;

import javax.servlet.Filter;
import java.util.Map;

/**
 * 自定义ShiroFilterFactoryBean解决资源中文路径问题
 */
@Slf4j
public class CustomShiroFilterFactoryBean extends ShiroFilterFactoryBean {

	@Override
	public Class getObjectType() {
		return MySpringShiroFilter.class;
	}

	@Override
	protected AbstractShiroFilter createInstance() throws Exception {
		SecurityManager securityManager = getSecurityManager();
		if (securityManager == null) {
			String msg = "SecurityManager property must be set.";
			throw new BeanInitializationException(msg);
		}
		if (!(securityManager instanceof WebSecurityManager)) {
			String msg = "The security manager does not implement the WebSecurityManager interface.";
			throw new BeanInitializationException(msg);
		}
		FilterChainManager manager = createFilterChainManager();
		//Expose the constructed FilterChainManager by first wrapping it in a
		// FilterChainResolver implementation. The AbstractShiroFilter implementations
		// do not know about FilterChainManagers - only resolvers:
		PathMatchingFilterChainResolver chainResolver = new PathMatchingFilterChainResolver();
		chainResolver.setFilterChainManager(manager);
		Map<String,Filter> filterMap = manager.getFilters();
		Filter invalidRequestFilter = filterMap.get(DefaultFilter.invalidRequest.name());
		if (invalidRequestFilter instanceof InvalidRequestFilter) {
			//此处是关键,设置false跳过URL携带中文400，servletPath中文校验bug
			((InvalidRequestFilter) invalidRequestFilter).setBlockNonAscii(false);
		}
		//Now create a concrete ShiroFilter instance and apply the acquired SecurityManager and built
		//FilterChainResolver.  It doesn't matter that the instance is an anonymous inner class
		//here - we're just using it because it is a concrete AbstractShiroFilter instance that accepts
		//injection of the SecurityManager and FilterChainResolver:
		return new MySpringShiroFilter((WebSecurityManager) securityManager, chainResolver);
	}

	private static final class MySpringShiroFilter extends AbstractShiroFilter {

		protected MySpringShiroFilter(WebSecurityManager webSecurityManager, FilterChainResolver resolver) {
			if (webSecurityManager == null) {
				throw new IllegalArgumentException("WebSecurityManager property cannot be null.");
			} else {
				this.setSecurityManager(webSecurityManager);
				if (resolver != null) {
					this.setFilterChainResolver(resolver);
				}
			}
		}
	}
}
